
#include "airc.h"

void airc_reply_hidereply(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_defaultreply(IRC_conn *irc, char *cmd, char *user, char **args);

void airc_reply_welcome(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_myinfo(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_isupport(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_luser(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_away(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_motd(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_whois(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_whoisserver(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_whowasuser(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_whoreply(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_list(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_namreply(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_endofwhois(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_notopic(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_topic(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_topicwhotime(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_whoismodes(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_nicknameinuse(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_watch(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_join(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_kick(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_mode(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_nick(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_notice(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_part(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_privmsg(IRC_conn *irc, char *cmd, char *user, char **args);
void airc_reply_quit(IRC_conn *irc, char *cmd, char *user, char **args);

struct _airc_reply {
    char *cmd;
    void (*reply_cb)(IRC_conn *irc, char *cmd, char *user, char **args);
};

#define IRC_REPLY(cmd, cb) {#cmd, airc_reply_ ## cb}

struct _airc_reply airc_replies[] = {
    IRC_REPLY(001, welcome),
    IRC_REPLY(002, welcome),
    IRC_REPLY(003, welcome),
    IRC_REPLY(004, myinfo),
    IRC_REPLY(005, isupport),
    IRC_REPLY(251, luser),
    IRC_REPLY(252, luser),
    IRC_REPLY(253, luser),
    IRC_REPLY(254, luser),
    IRC_REPLY(255, luser),
    IRC_REPLY(265, luser),
    IRC_REPLY(266, luser),
    IRC_REPLY(301, away),
    IRC_REPLY(305, hidereply),
    IRC_REPLY(306, hidereply),
    IRC_REPLY(307, whois),
    IRC_REPLY(308, whois),
    IRC_REPLY(309, whois),
    IRC_REPLY(310, whois),
    IRC_REPLY(311, whois),
    IRC_REPLY(312, whoisserver),
    IRC_REPLY(313, whois),
    IRC_REPLY(314, whowasuser),
    IRC_REPLY(315, whoreply),
    IRC_REPLY(316, whois),
    IRC_REPLY(317, whois),
    IRC_REPLY(318, endofwhois),
    IRC_REPLY(319, whois),
    IRC_REPLY(320, whois),
    IRC_REPLY(321, list),
    IRC_REPLY(322, list),
    IRC_REPLY(323, list),
    IRC_REPLY(330, whois),
    IRC_REPLY(331, notopic),
    IRC_REPLY(332, topic),
    IRC_REPLY(333, topicwhotime),
    IRC_REPLY(335, whois),
    IRC_REPLY(352, whoreply),
    IRC_REPLY(353, namreply),
    IRC_REPLY(366, hidereply),
    IRC_REPLY(368, whois),
    IRC_REPLY(369, endofwhois),
    IRC_REPLY(372, motd),
    IRC_REPLY(375, motd),
    IRC_REPLY(376, motd),
    IRC_REPLY(378, whois),
    IRC_REPLY(379, whoismodes),
    IRC_REPLY(396, defaultreply),
    IRC_REPLY(433, nicknameinuse),
    IRC_REPLY(598, watch),
    IRC_REPLY(599, watch),
    IRC_REPLY(600, watch),
    IRC_REPLY(601, watch),
    IRC_REPLY(604, watch),
    IRC_REPLY(605, watch),
    IRC_REPLY(609, watch),
    IRC_REPLY(671, whois),
    IRC_REPLY(JOIN, join),
    IRC_REPLY(KICK, kick),
    IRC_REPLY(MODE, mode),
    IRC_REPLY(NICK, nick),
    IRC_REPLY(NOTICE, notice),
    IRC_REPLY(PART, part),
    IRC_REPLY(PRIVMSG, privmsg),
    IRC_REPLY(QUIT, quit),
    IRC_REPLY(TOPIC, topic),
    {NULL, NULL}
};

#undef IRC_REPLY

static void airc_parse_failed(IRC_conn *irc, char *input){
    PurpleConnection *gc;
    char *clean;
    
    gc = purple_account_get_connection(irc->account);  
    /* This really should be escaped somehow that you can tell what
     * the junk was -- but as it is, it can crash glib. */
    clean = purple_utf8_salvage(input);
    purple_debug(PURPLE_DEBUG_WARNING, "airc",
        "Server sent unrecognized reply: \"%s\"\n", clean);
    if(gc && irc->features.network)
        serv_got_im(gc, irc->features.network, clean, 0, time(NULL));
    g_free(clean);
}

static char *airc_recv_convert(IRC_conn *irc, char *string){
    /* If there was anything the prpl-irc did nicely, it was this */
    char *utf8 = NULL;
    const gchar *charset, *enclist;
    gchar **encodings;
    int i;

    /*if(!g_utf8_validate(string, -1, NULL)){
        tmp = string;
        purple_debug_info("airc", "bad string: chars (");
        while(*tmp){
            purple_debug_info("airc", "%ud (%c), ", *tmp, *tmp);
            tmp++;
        }
        purple_debug_info("airc", ")\n");
        g_free(purple_utf8_strip_unprintables(string));
        
    }*/
    if(purple_account_get_bool(irc->account, "autodetect_utf8",
        IRC_DEFAULT_AUTODETECT) && g_utf8_validate(string, -1, NULL)){
        return g_strdup(string);
    }
    enclist = purple_account_get_string(irc->account, "encoding",
        IRC_DEFAULT_CHARSET);
    encodings = g_strsplit(enclist, ",", -1);
    if(encodings[0] == NULL){
        g_strfreev(encodings);
        return purple_utf8_salvage(string);
    }
    for(i = 0; encodings[i] != NULL; i++){
        charset = encodings[i];
        while(*charset == ' ')
            charset++;
        if (!g_ascii_strcasecmp("UTF-8", charset)) {
            if (g_utf8_validate(string, -1, NULL))
                utf8 = g_strdup(string);
        }else{
            utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL);
        }
        if(utf8){
            g_strfreev(encodings);
            return utf8;
        }
    }
    g_strfreev(encodings);
    return purple_utf8_salvage(string);
}

void airc_parse_msg(IRC_conn *irc, char *input){
    PurpleConnection *gc;
    char *tmp, *cmd, *user, *cur, *space, **args, *utf8;
    struct _airc_reply *areply;
    void (*reply_cb)(IRC_conn *irc, char *cmd, char *user, char **args);
    int numArgs = 0;
    
    irc->lastping = time(NULL);
    reply_cb = NULL;
    gc = purple_account_get_connection(irc->account);  
    purple_signal_emit(airc_plugin, "irc-receiving-text", gc, &input); 
    /*purple_debug_info("airc", "receiving: %s\n", input);*/
    if(!strncmp(input, "PING", 4)){
        irc_send(irc, "PONG%s", input + 4);
        return;
    }else if(!strncmp(input, "ERROR ", 6)){
        if(g_utf8_validate(input, -1, NULL)){
            tmp = g_strdup_printf("%s\n%s", "Disconnected.", input);
            purple_connection_error_reason(gc,
                PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
            g_free(tmp);
        }else{
            purple_connection_error_reason(gc,
                PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Disconnected.");
        }
        return;
    }
    cur = input;
    if(cur[0]!=':'){
        user = NULL;
        space = strchr(cur, ' ');
        if(!space){
            cmd = purple_utf8_salvage(cur);
            cur += strlen(cur);
        }else{
            tmp = g_strndup(cur, space-cur);
            cmd = purple_utf8_salvage(tmp);
            g_free(tmp);
            cur = space+1;
        }
    }else{
        cur++;
        space = strchr(cur, ' ');
        if(!space){
            airc_parse_failed(irc, input);
            return;
        }
        tmp = g_strndup(cur, space-cur);
        user = purple_utf8_salvage(tmp);
        g_free(tmp);
        cur = space+1;
        space = strchr(cur, ' ');
        if(!space){
            cmd = purple_utf8_salvage(cur);
            cur += strlen(cur);
        }else{
            tmp = g_strndup(cur, space-cur);
            cmd = purple_utf8_salvage(tmp);
            g_free(tmp);
            cur = space+1;
        }
    }
    areply = airc_replies;
    while(areply->cmd){
        if(!g_ascii_strcasecmp(areply->cmd, cmd)){
            reply_cb = areply->reply_cb;
            break;
        }
        areply++;
    }
    if(!reply_cb){
        airc_parse_failed(irc, input);
        g_free(cmd);
        g_free(user);
        return;
    }
    tmp = cur;
    if(cur[0] == ':'){
        numArgs = 1;
    }else{
        while(*cur){
            cur = strchr(cur, ' ');
            numArgs++;
            if(!cur)
                break;
            cur++;
            if(*cur==':'){
                numArgs++;
                break;
            }
        }
    }
    args = g_strsplit(tmp, " ", numArgs);
    if(numArgs > 0){
        tmp = args[numArgs-1];
        if(tmp[0] == ':'){
            utf8 = airc_recv_convert(irc, tmp+1);
            free(tmp);
            args[numArgs-1] = utf8;
        }
    }
    reply_cb(irc, cmd, user, args);
    g_strfreev(args);
    g_free(cmd);
    g_free(user);
}

static int mirc_colors[][3] = {
    {0xff,0xff,0xff},
    {0x00,0x00,0x00},
    {0x00,0x00,0x88},
    {0x00,0x88,0x00},
    {0xff,0x00,0x00},
    {0x88,0x00,0x00},
    {0x88,0x00,0x88},
    {0xff,0x88,0x00},
    {0xff,0xff,0x00},
    {0x00,0xff,0x00},
    {0x00,0x88,0x88},
    {0x00,0xff,0xff},
    {0x00,0x00,0xff},
    {0xff,0x00,0xff},
    {0x88,0x88,0x88},
    {0xcc,0xcc,0xcc}
};

void airc_number_to_color(char *colorcode, int num){
    /* char colorcode[8] */
    num = num%16;
    sprintf(colorcode, "#%02X%02X%02X", mirc_colors[num][0],
        mirc_colors[num][1], mirc_colors[num][2]);
}

char *airc_parse_mirc_to_txt(char *input){
    GString *output;
    char c;
    
    output = g_string_sized_new(strlen(input));
    while((c = *(input++))){
        switch(c){
            case 0x2:
            case 0x16:
            case 0x1d:
            case 0x1f:
            case 0xf:
                break;
            case 0x3:
                if(!g_ascii_isdigit(input[0])){
                    if(input[0] == ','){
                        if(g_ascii_isdigit(input[1])){
                            input += 2;
                            if(g_ascii_isdigit(input[0]))
                                input++;
                            break;
                        }
                    }
                    break;
                }
                input++;
                if(g_ascii_isdigit(input[0]))
                    input++;
                if((input[0] != ',') || (!g_ascii_isdigit(input[1])))
                    break;
                input += 2;
                if(g_ascii_isdigit(input[0]))
                    input++;
                break;
            default:
                g_string_append_c(output, c);
                break;
        }
    }   
    return g_string_free(output, FALSE);    
}

char *airc_parse_mirc_to_html(char *input){
    GString *output;
    char c, colors[2][3] = {"\0\0", "\0\0"};
    char fgcolor[8], bgcolor[8];
    gboolean bold, italic, underline, font;
    bold = italic = underline = font = FALSE;
    
    output = g_string_sized_new(strlen(input));
    while((c = *(input++))){
        switch(c){
            case 0x2:
                if(bold){
                    g_string_append(output, "</B>");
                    bold = FALSE;
                }else{
                    g_string_append(output, "<B>");
                    bold = TRUE;
                }
                break;
            case 0x3:
                if(!g_ascii_isdigit(input[0])){
                    if(input[0] == ','){
                        if(g_ascii_isdigit(input[1])){
                            colors[1][0] = input[1];
                            input += 2;
                            if(g_ascii_isdigit(input[0])){
                                colors[1][1] = input[0];
                                input++;
                            }
                            if(font)
                                g_string_append(output, "</FONT>");
                            airc_number_to_color(bgcolor, atoi(colors[1]));
                            g_string_append_printf(output, "<FONT BACK=\"%s\">",
                                bgcolor);
                            colors[1][0] = '\0';
                            colors[1][1] = '\0';
                            font = TRUE;
                            break;
                        }
                    }
                    if(font)
                        g_string_append(output, "</FONT>");
                    font = FALSE;
                    break;
                }
                colors[0][0] = input[0];
                input++;
                if(g_ascii_isdigit(input[0])){
                    colors[0][1] = input[0];
                    input++;
                }
                if((input[0] != ',') || (!g_ascii_isdigit(input[1]))){
                    if(font)
                        g_string_append(output, "</FONT>");
                    airc_number_to_color(fgcolor, atoi(colors[0]));
                    g_string_append_printf(output, "<FONT COLOR=\"%s\">",
                        fgcolor);
                    colors[0][0] = '\0';
                    colors[0][1] = '\0';
                    font = TRUE;
                    break;
                }
                input++;
                colors[1][0] = input[0];
                input++;
                if(g_ascii_isdigit(input[0])){
                    colors[1][1] = input[0];
                    input++;
                }
                if(font)
                    g_string_append(output, "</FONT>");
                airc_number_to_color(fgcolor, atoi(colors[0]));
                airc_number_to_color(bgcolor, atoi(colors[1]));
                g_string_append_printf(output, "<FONT COLOR=\"%s\" BACK=\"%s\">",
                    fgcolor, bgcolor);
                colors[0][0] = '\0';
                colors[0][1] = '\0';
                colors[1][0] = '\0';
                colors[1][1] = '\0';
                font = TRUE;
                break;
            case 0x16:
                /* TODO reverses */
            case 0x1d:
                if(italic){
                    g_string_append(output, "</I>");
                    italic = FALSE;
                }else{
                    g_string_append(output, "<I>");
                    italic = TRUE;
                }
                break;
            case 0x1f:
                if(underline){
                    g_string_append(output, "</U>");
                    underline = FALSE;
                }else{
                    g_string_append(output, "<U>");
                    underline = TRUE;
                }
                break;
            case 0xf:
                if(bold){
                    g_string_append(output, "</B>");
                    bold = FALSE;
                }
                if(italic){
                    g_string_append(output, "</I>");
                    italic = FALSE;
                }
                if(underline){
                    g_string_append(output, "</U>");
                    underline = FALSE;
                }
                if(font){
                    g_string_append(output, "</FONT>");
                    font = FALSE;
                }
                break;
            default:
                g_string_append_c(output, c);
                break;
        }
    }
    if(bold){
        g_string_append(output, "</B>");
        bold = FALSE;
    }
    if(italic){
        g_string_append(output, "</I>");
        italic = FALSE;
    }
    if(underline){
        g_string_append(output, "</U>");
        underline = FALSE;
    }
    if(font){
        g_string_append(output, "</FONT>");
        font = FALSE;
    }
    return g_string_free(output, FALSE);
}

char *airc_parse_ctcp(IRC_conn *irc, char *sender, char *target, char *msg, gboolean *show){
    char *arg = NULL, **tmpar;
    
    if((msg[0] != 0x1) && (msg[strlen(msg)-1] != 0x1))
        return NULL;
    msg[strlen(msg)-1] = '\0';
    msg++;
    if(!strncmp(msg, "ACTION", 6)){
        arg = g_strdup_printf("/me %s", msg+7);
    }else{
        *show = TRUE;
        tmpar = g_strsplit(msg, "\\n", -1);
        arg = g_strjoinv("\n", tmpar);
        g_strfreev(tmpar);
    }
    return arg;
}

char *airc_escape_privmsg(char *msg){
    GString *str;
    char c, *next;
    
    str = g_string_sized_new(strlen(msg));
    next = msg;
    while((c = *next)){
        switch(c){
        case '&':
            g_string_append(str, "&amp;");
            break;
        case '<':
            g_string_append(str, "&lt;");
            break;
        case '>':
            g_string_append(str, "&gt;");
            break;
        case '\'':
            g_string_append(str, "&apos;");
            break;
        case '"':
            g_string_append(str, "&quot;");
            break;
        case '\n':
            g_string_append(str, "<br>");
            break;
        default:
            g_string_append_c(str, c);
            break;
        }
        next = g_utf8_next_char(next);
    }
    return g_string_free(str, FALSE);
}

void airc_reply_hidereply(IRC_conn *irc, char *cmd, char *user, char **args){
    return;
}

void airc_reply_defaultreply(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    char *msg, *tmp, *colored;
    
    gc = purple_account_get_connection(irc->account);
    tmp = g_strjoinv(" ", args+1);
    msg = airc_escape_privmsg(tmp);
    g_free(tmp);
    colored = airc_parse_mirc_to_html(msg);
    g_free(msg);
    if(gc && irc->features.network)
        serv_got_im(gc, irc->features.network, colored, 0, time(NULL));
    g_free(colored);
}

void airc_on_pre_connect(IRC_conn *irc){
    GSList *buddies;
    PurpleBuddy *b;
    IRC_buddy *ib;
    
    if(irc->requests.preconnected)
        return;
    for(buddies = purple_find_buddies(irc->account, NULL); buddies;
            buddies = g_slist_delete_link(buddies, buddies)){
        b = buddies->data;
        ib = g_new0(IRC_buddy, 1);
        ib->name = g_strdup(purple_buddy_get_name(b));
        ib->ref = 1;
        g_hash_table_replace(irc->buddies, ib->name, ib);
        if(irc->features.watch)
            irc_send(irc, "WATCH +%s", ib->name);
    }
    irc->requests.preconnected = TRUE;
}

void airc_on_connect(IRC_conn *irc){
    char **concmds, **concmdar, *tmp;
    
    if(irc->requests.connected)
        return;
    concmdar = concmds = g_strsplit(purple_account_get_string(
        irc->account, "connectcmd", ""), "&&/", -1);
    while((tmp = *(concmds++))){
        irc_send(irc, "%s", tmp);
    }
    g_strfreev(concmdar);
    irc->requests.connected = TRUE;
}

void airc_reply_welcome(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    
    gc = purple_account_get_connection(irc->account);
    if(gc && (purple_connection_get_state(gc) == PURPLE_CONNECTED))
        return;
    if(irc->nick)
        return;
    irc->nick = g_strdup(args[0]);
    purple_connection_set_display_name(gc, args[0]);
    purple_connection_set_state(gc, PURPLE_CONNECTED);
}

void airc_reply_myinfo(IRC_conn *irc, char *cmd, char *user, char **args){
    return;
}

void airc_reply_isupport(IRC_conn *irc, char *cmd, char *user, char **args){
    char *feature, *end;
    
    while((feature = *args)){
        if(!strncmp(feature, "PREFIX=", 7)){
            if((end = strchr(feature, ')')) && (!irc->features.prefix))
                irc->features.prefix = g_strndup(feature+8, end-feature-8);
        }else if(!strncmp(feature, "STATUSMSG=", 10)){
            if(!irc->features.statusmsg)
                irc->features.statusmsg = g_strdup(feature+10);
        }else if(!strncmp(feature, "NETWORK=", 8)){
            if(!irc->features.network)
                irc->features.network = g_strdup(feature+8);
        }else if(!strncmp(feature, "CHANTYPES=", 10)){
            if(!irc->features.chantypes)
                irc->features.chantypes = g_strdup(feature+10);
        }else if(!strncmp(feature, "CHANMODES=", 10)){
            if(!irc->features.chanmodes)
                irc->features.chanmodes = g_strsplit(feature+10, ",", 4);
        }else if(!strncmp(feature, "NAMESX", 6)){
            irc_send(irc, "PROTOCTL NAMESX");
        }else if(!strncmp(feature, "WATCH", 5)){
            irc->features.watch = TRUE;
            airc_on_pre_connect(irc);
        }
        args++;
    }
}

void airc_reply_luser(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    char *msg;
    
    gc = purple_account_get_connection(irc->account);
    if(!args || !args[0] || !args[1] || !gc || !irc->features.network)
        return;
    if(args[2])
        msg = g_strdup_printf("%s %s", args[1], args[2]);
    else
        msg = g_strdup(args[1]);
    airc_on_connect(irc);
    serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
    free(msg);
}

void airc_reply_away(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    char *msg, *colored, *tmp, *tmp2;
    PurpleBuddy *buddy;
    PurplePresence *presence;
    PurpleStatus *status;

    if (!args || !args[1])
        return;
    if((irc->requests.whois == WHOIS_FETCH) && (irc->dialogs.whois)){
        msg = g_markup_escape_text(args[2], -1);
        colored = airc_parse_mirc_to_html(msg);
        g_free(msg);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Away", colored);
        g_free(colored);
        return;
    }
    gc = purple_account_get_connection(irc->account);
    if(!gc)
        return;
    msg = g_markup_escape_text(args[2], -1);
    if((buddy = purple_find_buddy(irc->account, args[1]))){
        presence = purple_buddy_get_presence(buddy);
        status = purple_presence_get_status(presence, "away");
        tmp = (char *)purple_status_get_attr_string(status, "message");
        if(!tmp || strcmp(msg, tmp)){
            tmp2 = g_strdup(msg);
            purple_prpl_got_user_status(irc->account, args[1], "away",
                "message", tmp2, NULL);
            colored = airc_parse_mirc_to_html(msg);
            serv_got_im(gc, args[1], msg, PURPLE_MESSAGE_AUTO_RESP, time(NULL));
            g_free(colored);
            return;
        }
        g_free(msg);
        return;
    }
    colored = airc_parse_mirc_to_html(msg);
    g_free(msg);
    serv_got_im(gc, args[1], colored, PURPLE_MESSAGE_AUTO_RESP, time(NULL));
    g_free(colored);
}

void airc_reply_list(IRC_conn *irc, char *cmd, char *from, char **args){
    PurpleRoomlistRoom *room;
    char *topic;
    
    if(!irc->roomlist)
        return;
    if(!strcmp(cmd, "321")){
        purple_roomlist_set_in_progress(irc->roomlist, TRUE);
        return;
    }
    if(!strcmp(cmd, "322")){
        if(!args[0] || !args[1] || !args[2] || !args[3])
            return;
        if(!purple_roomlist_get_in_progress(irc->roomlist))
            purple_roomlist_set_in_progress(irc->roomlist, TRUE);
        room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, args[1], NULL);
        purple_roomlist_room_add_field(irc->roomlist, room, args[1]);
        purple_roomlist_room_add_field(irc->roomlist, room, GINT_TO_POINTER(strtol(args[2], NULL, 10)));
        topic = airc_parse_mirc_to_txt(args[3]);
        purple_roomlist_room_add_field(irc->roomlist, room, topic);
        g_free(topic);
        purple_roomlist_room_add(irc->roomlist, room);
    }
    if(!strcmp(cmd, "323")){
        purple_roomlist_set_in_progress(irc->roomlist, FALSE);
        purple_roomlist_unref(irc->roomlist);
        irc->roomlist = NULL;
        return;
    }
}

void airc_reply_notopic(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    char *msg;
    
    gc = purple_account_get_connection(irc->account);
    if(!gc || !args || !args[0])
        return;
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        args[0], irc->account);
    msg = g_strdup_printf("%s has no topic set.", args[0]);
    if(convo)
        purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
    else if(irc->features.network)
        serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
    g_free(msg);
}

void airc_reply_topic(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    char *topic, *chan, *msg, *colored;
    IRC_splithost *splithost = NULL;
    
    gc = purple_account_get_connection(irc->account);
    if(!gc || !args)
        return;
    if(strcasecmp(cmd, "TOPIC")){/* 332 */
        chan = args[1];
        colored = airc_parse_mirc_to_html(topic = args[2]);
        msg = g_strdup_printf("The topic for %s is: %s", chan, colored);
        g_free(colored);
    }else{
        chan = args[0];
        colored = airc_parse_mirc_to_html(topic = args[2]);
        splithost = airc_split_host(irc, user);
        msg = g_strdup_printf("%s changed the topic of %s to %s",
            splithost->nick, chan, colored);
        g_free(colored);
    }
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, chan,
        irc->account);
    if(convo){
        purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
            (splithost) ? splithost->nick : NULL, topic);
        purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
    }else{
        if(irc->features.network)
            serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
    }
    g_free(msg);
}

void airc_reply_topicwhotime(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    char *msg;
    time_t lastEdit;
    
    gc = purple_account_get_connection(irc->account);
    if(!gc || !args || !args[0] || !args[1] || !args[2] || !args[3])
        return;
    lastEdit = atoi(args[3]);
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        args[1], irc->account);
    msg = g_strdup_printf("The topic for %s was last edited by %s at %s.",
        args[1], args[2],
        purple_date_format_full(localtime(&lastEdit)));
    if(convo)
        purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
    else if(irc->features.network)
        serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
    g_free(msg);
}

void airc_reply_whois(IRC_conn *irc, char *cmd, char *user, char **args){
    char *tmp;
    time_t when;
    
    irc->requests.whois = WHOIS_FETCH;
    if(!irc->dialogs.whois){
        irc->dialogs.whois = purple_notify_user_info_new();
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Nick", args[1]);
    }
    if(!strcmp(cmd, "311")){
        tmp = g_strdup_printf("%s@%s", args[2], args[3]);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Userhost", tmp);
        g_free(tmp);
        tmp = airc_parse_mirc_to_html(args[5]);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Realname", tmp);
        g_free(tmp);
    }else if(!strcmp(cmd, "317")){
        tmp = purple_str_seconds_to_string(atoi(args[2]));
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Idle", tmp);
        g_free(tmp);
        if(args[3]){
            when = (time_t)atoi(args[3]);
            purple_notify_user_info_add_pair(irc->dialogs.whois, "Online",
                purple_date_format_full(localtime(&when)));
        }
    }else if(!strcmp(cmd, "319")){
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Channels", args[2]);
    }else if(!strcmp(cmd, "330")){
        tmp = g_strjoin(" ", args[1], args[3], args[2], NULL);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Info", tmp);
        g_free(tmp);
    }else{
        tmp = g_strjoinv(" ", args+1);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Info", tmp);
        g_free(tmp);
    }
}

void airc_reply_whoisserver(IRC_conn *irc, char *cmd, char *user, char **args){
    /* 312: this RPL conflicts in WHOIS and WHOWAS */
    char *tmp;
    
    if(irc->requests.whois == WHOWAS_FETCH){
        if(!irc->dialogs.whois){
            irc->dialogs.whois = purple_notify_user_info_new();
            purple_notify_user_info_add_pair(irc->dialogs.whois, "Nick", args[1]);
        }
        if(!args[2])
            return;
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Server", args[2]);
        if(!args[3])
            return;
        purple_notify_user_info_add_pair(irc->dialogs.whois, "When", args[3]);
    }else if(irc->requests.whois == WHOIS_FETCH){
        tmp = g_strdup_printf("%s (%s)", args[2], args[3]);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Server", tmp);
        g_free(tmp);
    }
}

void airc_reply_whoismodes(IRC_conn *irc, char *cmd, char *user, char **args){
    /* 379: Another conflict */
    char *tmp;
    
    if(irc->requests.whois == WHOWAS_FETCH){
        if(!irc->dialogs.whois){
            irc->dialogs.whois = purple_notify_user_info_new();
            purple_notify_user_info_add_pair(irc->dialogs.whois, "Nick", args[1]);
        }
        tmp = g_strjoinv(" ", args+1);
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Host", tmp);
        g_free(tmp);
    }else if(irc->requests.whois == WHOIS_FETCH){
        
    }
}


void airc_reply_whowasuser(IRC_conn *irc, char *cmd, char *user, char **args){
    char *tmp;
    
    irc->requests.whois = WHOWAS_FETCH;
    if(!irc->dialogs.whois){
        irc->dialogs.whois = purple_notify_user_info_new();
        purple_notify_user_info_add_pair(irc->dialogs.whois, "Nick", args[1]);
    }
    purple_notify_user_info_add_section_break(irc->dialogs.whois);
    tmp = g_strdup_printf("%s@%s", args[2], args[3]);
    purple_notify_user_info_add_pair(irc->dialogs.whois, "Userhost", tmp);
    g_free(tmp);
    tmp = airc_parse_mirc_to_html(args[5]);
    purple_notify_user_info_add_pair(irc->dialogs.whois, "Realname", tmp);
    g_free(tmp);
    
}

void airc_reply_whoreply(IRC_conn *irc, char *cmd, char *user, char **args){
    if(!strcmp(cmd, "315")){
        irc->requests.who = WHO_SUPPRESS;
        return;
    }
    if(irc->requests.who == WHO_SUPPRESS)
        return;
}

void airc_reply_endofwhois(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    
    gc = purple_account_get_connection(irc->account);
    if(!args || !gc || !irc->dialogs.whois)
        return;
    purple_notify_userinfo(gc, args[1], irc->dialogs.whois, NULL, NULL);
    purple_notify_user_info_destroy(irc->dialogs.whois);
    irc->dialogs.whois = NULL;
    irc->requests.whois = WHOIS_SUPPRESS;
}

void airc_reply_namreply(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    PurpleConvChat *chat;
    char *msg, **namear, **names, *name, *tmp, *hostpart;
    int flag, cstatus;
    GList *users = NULL, *flags = NULL;
    
    gc = purple_account_get_connection(irc->account);
    if(!args || !gc)
        return;
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        args[2], irc->account);
    msg = g_strdup_printf("Users on %s: %s", args[2], args[3]);
    namear = g_strsplit(args[3], " ", -1);
    if(convo){
        chat = PURPLE_CONV_CHAT(convo);
        purple_conv_chat_write(chat, "", msg, PURPLE_MESSAGE_SYSTEM,
            time(NULL));
        names = namear;
        while((name = *(names++)) && (*name)){
            flag = 0;
            while((cstatus = airc_get_flag_from_statuschar(irc, *name))){
                flag |= cstatus;
                name++;
            }
            if(!purple_conv_chat_find_user(chat, name)){
                /* uhnames will NOT break this client */
                if((hostpart = strchr(name, '!'))){
                    tmp = g_strndup(name, tmp-name);
                }else{
                    tmp = g_strdup(name);
                }
                users = g_list_prepend(users, tmp);
                flags = g_list_prepend(flags, GINT_TO_POINTER(flag));
            }
        }
        if(users){
            purple_conv_chat_add_users(chat, users, NULL, flags, FALSE);
            g_list_free_full(users, g_free);
            g_list_free(flags);
        }
    }else{
        if(irc->features.network)
            serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
    }
    
    g_free(msg);
    g_strfreev(namear);
}

void airc_reply_motd(IRC_conn *irc, char *cmd, char *user, char **args){
    if(!strcmp(cmd, "376")){
        airc_on_connect(irc);
        irc->requests.motd = MOTD_SUPPRESS;
        return;
    }
    if(!strcmp(cmd, "375")){
        /* might use this to "open" a MOTD window */
        return;
    }
    /* maybe we should show the MOTD on connect initially, and then let users
       suppress it later on. */
    if(irc->requests.motd == MOTD_SUPPRESS)
        return;
}

void airc_reply_nicknameinuse(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    
    if(g_strv_length(args)<2)
        return;
    gc = purple_account_get_connection(irc->account);
    if(gc && (purple_connection_get_state(gc) == PURPLE_CONNECTED)){
        return;
    }
    /* attempt to get a nick so that you can connect */
    irc_send(irc, "NICK %s_", args[1]);
    return;
}

void airc_reply_watch(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleBuddy *buddy;
    const char *authnick, *nickpass;
    
    if(g_strv_length(args)<4)
        return;
    if(!strcmp(cmd, "604") &&
        (authnick = purple_account_get_string(irc->account, "authnick", NULL))
        && (!strcasecmp(authnick, args[1])) &&
        (nickpass = purple_account_get_string(irc->account, "nickpass", NULL))){
            irc_send(irc, "PRIVMSG %s :IDENTIFY %s", authnick, nickpass);
    }
    if(!strcmp(cmd, "599")){
        if(!(buddy = purple_find_buddy(irc->account, args[0])))
            return;
        purple_prpl_got_user_status(irc->account, args[0], "available", NULL);
        return;
    }
    if(!strcmp(cmd, "598")){
        if(!(buddy = purple_find_buddy(irc->account, args[0])))
            return;
        purple_prpl_got_user_status(irc->account, args[0], "away", "message", args[4], NULL);
        return;
    }
    if(!strcmp(cmd, "600")){
        if(!(buddy = purple_find_buddy(irc->account, args[1])))
            return;
        purple_prpl_got_user_status(irc->account, args[1], "available", NULL);
        return;
    }
    if(!strcmp(cmd, "601")){
        if(!(buddy = purple_find_buddy(irc->account, args[1])))
            return;
        purple_prpl_got_user_status(irc->account, args[1], "offline", NULL);
        return;
    }
    if(!strcmp(cmd, "604")){
        if(!(buddy = purple_find_buddy(irc->account, args[1])))
            return;
        purple_prpl_got_user_status(irc->account, args[1], "available", NULL);
        return;
    }
    if(!strcmp(cmd, "605")){
        if(!(buddy = purple_find_buddy(irc->account, args[1])))
            return;
        purple_prpl_got_user_status(irc->account, args[1], "offline", NULL);
        return;
    }
    if(!strcmp(cmd, "609")){
        if(!(buddy = purple_find_buddy(irc->account, args[1])))
            return;
        purple_prpl_got_user_status(irc->account, args[1], "away", NULL);
        return;
    }
}

void airc_reply_join(IRC_conn *irc, char *cmd, char *user, char **args){
    IRC_splithost *splithost;
    PurpleConnection *gc;
    PurpleConversation *convo;
    PurpleConvChat *chat;
    static int cid = 1;
    char *userhost;
    PurpleConvChatBuddy *cb;
    
    gc = purple_account_get_connection(irc->account);
    if(!args[0] || !gc)
        return;
    splithost = airc_split_host(irc, user);
    if(!purple_utf8_strcasecmp(splithost->nick,
        purple_connection_get_display_name(gc))){
        serv_got_joined_chat(gc, cid++, args[0]);
        free_splithost(splithost);
        return;
    }
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        args[0], irc->account);
    chat = PURPLE_CONV_CHAT(convo);
    userhost = g_strdup_printf("%s@%s", splithost->ident, splithost->host);
    purple_conv_chat_add_user(chat, splithost->nick, userhost, PURPLE_CBFLAGS_NONE, TRUE);
    cb = purple_conv_chat_cb_find(chat, splithost->nick);
    if(cb)
        purple_conv_chat_cb_set_attribute(chat, cb, "userhost", userhost);
    g_free(userhost);
    free_splithost(splithost);
}

void airc_reply_kick(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    IRC_splithost *splithost;
    char *msg, *channel;
    
    gc = purple_account_get_connection(irc->account);
    if(!gc || !args || !args[0] || !args[1])
        return;
    channel = args[0];
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        channel, irc->account);
    if(!convo)
        return;
    splithost = airc_split_host(irc, user);
    if(!purple_utf8_strcasecmp(args[1],
        purple_connection_get_display_name(gc))){
        if(args[2])
            msg = g_strdup_printf("You have been kicked by %s: %s", splithost->nick,
                args[2]);
        else
            msg = g_strdup_printf("You have been kicked by %s", splithost->nick);
        purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel, msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
        g_free(msg);
        serv_got_chat_left(gc, purple_conv_chat_get_id(
            PURPLE_CONV_CHAT(convo)));
    }else{
        if(args[2])
            msg = g_strdup_printf("Kicked by %s (%s)", splithost->nick, args[2]);
        else
            msg = g_strdup_printf("Kicked by %s", splithost->nick);
        purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
            args[1], msg);
    }
    free_splithost(splithost);
}

void airc_reply_mode(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    PurpleConvChat *chat = NULL;
    IRC_splithost *splithost;
    char *msg, *argStr, *target, mode, *modes, *arg;
    int param, flag, cflag;
    gboolean adding = TRUE;
    
    gc = purple_account_get_connection(irc->account);
    if(!args || !args[0] || !args[1] || !gc)
        return;
    splithost = airc_split_host(irc, user);
    target = args[0];
    modes = args[1];
    param = 2;
    if(airc_is_channel(irc, target)){
        if((convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
            target, irc->account)))
            chat = PURPLE_CONV_CHAT(convo);
        while((mode = *(modes++))){
            if(mode == '-'){
                adding = FALSE;
                continue;
            }
            if(mode == '+'){
                adding = TRUE;
                continue;
            }
            switch(airc_get_mode_class(irc, mode)){
                case 0:
                    param++;
                    break;
                case 1:
                    arg = args[param++];
                    if((flag = airc_get_flag_from_mode(irc, mode)) && chat){
                        cflag = purple_conv_chat_user_get_flags(chat, arg);
                        if(adding)
                            cflag |= flag;
                        else
                            cflag &= ~flag;
                        purple_conv_chat_user_set_flags(chat, arg, cflag);
                    }
                    break;
                case 2:
                    if(adding)
                        param++;
                    break;
                default:
                    break;
            }
        }
        argStr = g_strjoinv(" ", args+1);
        if(chat){
            msg = g_strdup_printf("%s set modes: %s", splithost->nick, argStr);
            purple_conv_chat_write(chat, "", msg, PURPLE_MESSAGE_SYSTEM,
                time(NULL));
        }else{
            msg = g_strdup_printf("%s set modes on %s: %s", splithost->nick,
                target, argStr);
            purple_conv_chat_write(chat, "", msg, PURPLE_MESSAGE_SYSTEM,
                time(NULL));
            serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
        }
    }else{
        argStr = g_strjoinv(" ", args+1);
        msg = g_strdup_printf("%s set modes on %s: %s", splithost->nick,
            target, argStr);
        purple_conv_chat_write(chat, "", msg, PURPLE_MESSAGE_SYSTEM,
            time(NULL));
        serv_got_im(gc, irc->features.network, msg, 0, time(NULL));
        
    }
    g_free(argStr);
    g_free(msg);
    free_splithost(splithost);
}

void airc_reply_nick(IRC_conn *irc, char *cmd, char *user, char **args){
    IRC_splithost *splithost;
    PurpleConnection *gc;
    PurpleConvChat *chat;
    PurpleConversation *conv;
    GSList *chats;
    
    gc = purple_account_get_connection(irc->account);
    if(!args[0] || !gc)
        return;
    splithost = airc_split_host(irc, user);
    if(!purple_utf8_strcasecmp(splithost->nick,
        purple_connection_get_display_name(gc))){
        if(irc->nick)
            g_free(irc->nick);
        irc->nick = g_strdup(args[0]);
        purple_connection_set_display_name(gc, args[0]);
    }
    chats = gc->buddy_chats;
    while(chats){
        chat = PURPLE_CONV_CHAT(chats->data);
        if(purple_conv_chat_find_user(chat, splithost->nick))
            purple_conv_chat_rename_user(chat, splithost->nick, args[0]);
        chats = chats->next;
    }
    if((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
        splithost->nick, irc->account)))
        purple_conversation_set_name(conv, args[0]);
    free_splithost(splithost);
}

void airc_reply_notice(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    IRC_splithost *splithost;
    char *msg, *target, *colored, *channel, *tmp;
    gboolean toChannel = FALSE;
    
    splithost = airc_split_host(irc, user);
    target = airc_nick_skip_mode(irc, args[0]);
    gc = purple_account_get_connection(irc->account);
    if(airc_is_channel(irc, target)){
        if(target == args[0])
            msg = g_strdup_printf("(notice) %s", args[1]);
        else
            msg = g_strdup_printf("(notice to %s) %s", args[0], args[1]);
        convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
            target, irc->account);
        tmp = airc_escape_privmsg(msg);
        g_free(msg);
        colored = airc_parse_mirc_to_html(tmp);
        g_free(tmp);
        if(convo){
            serv_got_chat_in(gc,
                purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
                splithost->nick, 0, colored, time(NULL));
            toChannel = TRUE;
        }
        g_free(colored);
    }
    if(!toChannel){
        if((args[1][0] == '[')&&(airc_is_channel(irc, args[1]+1))
            &&(tmp = strchr(args[1], ']'))){
            channel = g_strndup(args[1]+1, tmp-args[1]-1);
            convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
                channel, irc->account);
            g_free(channel);
            msg = g_strdup_printf("(notice to %s) %s", args[0], args[1]);
            if(convo){
                tmp = airc_escape_privmsg(msg);
                g_free(msg);
                colored = airc_parse_mirc_to_html(tmp);
                g_free(tmp);
                serv_got_chat_in(gc,
                    purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
                    splithost->nick, 0, colored, time(NULL));
                g_free(colored);
                toChannel = TRUE;
            }
        }
        if(!toChannel){
            if(!purple_utf8_strcasecmp(target,
                purple_connection_get_display_name(gc)))
                msg = g_strdup_printf("(notice) %s", args[1]);
            else
                msg = g_strdup_printf("(notice to %s) %s", args[0], args[1]);
            tmp = airc_escape_privmsg(msg);
            g_free(msg);
            colored = airc_parse_mirc_to_html(tmp);
            g_free(tmp);
            serv_got_im(gc, splithost->nick, colored, 0, time(NULL));
            g_free(colored);
        }
    }
    free_splithost(splithost);
}

void airc_reply_part(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    IRC_splithost *splithost;
    char *msg, *channel;
    
    gc = purple_account_get_connection(irc->account);
    channel = args[0];
    convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
        channel, irc->account);
    if(!gc || !convo)
        return;
    splithost = airc_split_host(irc, user);
    if(!purple_utf8_strcasecmp(splithost->nick,
        purple_connection_get_display_name(gc))){
        if(args[1])
            msg = g_strdup_printf("You have parted the channel: %s", 
                args[1]);
        else
            msg = g_strdup_printf("You have parted the channel");
        purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel, msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
        g_free(msg);
        serv_got_chat_left(gc, purple_conv_chat_get_id(
            PURPLE_CONV_CHAT(convo)));
    }else{
        if(args[1])
            purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
                splithost->nick, args[1]);
        else
            purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
                splithost->nick, NULL);
    }
    free_splithost(splithost);
}

void airc_reply_privmsg(IRC_conn *irc, char *cmd, char *user, char **args){
    PurpleConnection *gc;
    PurpleConversation *convo;
    IRC_splithost *splithost;
    char *tmp, *msg, *target, *colored;
    gboolean showCTCP = FALSE;
    
    splithost = airc_split_host(irc, user);
    target = airc_nick_skip_mode(irc, args[0]);
    gc = purple_account_get_connection(irc->account);
    msg = airc_parse_ctcp(irc, splithost->nick, target, args[1], &showCTCP);
    if(msg){
        tmp = airc_escape_privmsg(msg);
        g_free(msg);
    }else{
        tmp = airc_escape_privmsg(args[1]);
    }
    if(airc_is_channel(irc, target)){
        if(showCTCP){
                msg = g_strdup_printf("(ctcp to %s) %s", args[0], tmp);
        }else{
            if(target == args[0])
                msg = g_strdup(tmp);
            else
                msg = g_strdup_printf("(privmsg to %s) %s", args[0], tmp);
        }
        convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
            target, irc->account);
        colored = airc_parse_mirc_to_html(msg);
        if(convo)
            serv_got_chat_in(gc,
                purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
                splithost->nick, 0, colored, time(NULL));
    }else{
        if(showCTCP){
                msg = g_strdup_printf("(ctcp to %s) %s", args[0], tmp);
        }else{
            if(!purple_utf8_strcasecmp(target,
                purple_connection_get_display_name(gc)))
                msg = g_strdup(tmp);
            else
                msg = g_strdup_printf("(privmsg to %s) %s", args[0], tmp);
        }
        colored = airc_parse_mirc_to_html(msg);
        serv_got_im(gc, splithost->nick, colored, 0, time(NULL));
    }
    g_free(tmp);
    g_free(msg);
    g_free(colored);
    free_splithost(splithost);
}

void airc_reply_quit(IRC_conn *irc, char *cmd, char *user, char **args){
    IRC_splithost *splithost;
    PurpleConnection *gc;
    PurpleConvChat *chat;
    PurpleConversation *conv;
    GSList *chats;
    char *msg, *nick, *tmp;
    
    gc = purple_account_get_connection(irc->account);
    if(!args || !args[0] || !gc)
        return;
    splithost = airc_split_host(irc, user);
    nick = splithost->nick;
    chats = gc->buddy_chats;
    while(chats){
        chat = PURPLE_CONV_CHAT(chats->data);
        if(purple_conv_chat_find_user(chat, nick))
            purple_conv_chat_remove_user(chat, nick, args[0]);
        chats = chats->next;
    }
    if((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
        nick, irc->account))){
        msg = g_markup_escape_text(args[0], -1);
        tmp = airc_parse_mirc_to_html(msg);
        g_free(msg);
        msg = g_strdup_printf("%s has quit (%s)", nick, tmp);
        g_free(tmp);
        purple_conv_im_write(PURPLE_CONV_IM(conv), "", msg,
            PURPLE_MESSAGE_SYSTEM, time(NULL));
        g_free(msg);
    }
    free_splithost(splithost);
    
}
char *airc_nick_skip_mode(IRC_conn *irc, char *nick){
    static const char *default_modes = "@+%&";

    while(strchr(irc->features.statusmsg ? irc->features.statusmsg :
        default_modes, *nick) != NULL)
        nick++;
    return nick;
}

gboolean airc_is_channel(IRC_conn *irc, char *target){
    static const char *default_chantypes = "#&";
    
    if(!target)
        return FALSE;
    return (strchr((irc->features.chantypes) ? irc->features.chantypes :
        default_chantypes, target[0])) ? TRUE : FALSE;
}

IRC_splithost *airc_split_host(IRC_conn *irc, char *userhost){
    IRC_splithost *splithost;
    char *excl, *at;
    
    if(!userhost)
        return NULL;
    splithost = g_new0(IRC_splithost, 1);
    if(!splithost)
        return NULL;
    excl = strchr(userhost, '!');
    if(!excl){/* server name */
        splithost->nick = g_strdup(userhost);
        return splithost;
    }
    splithost->nick = g_strndup(userhost, excl-userhost);
    at = strchr(userhost, '@');
    if(!at){
        splithost->host = g_strdup(excl+1);
        return splithost;
    }
    splithost->ident = g_strndup(excl+1, at-excl-1);
    splithost->host = g_strdup(at+1);
    return splithost;
}

void free_splithost(IRC_splithost *userhost){
    if(!userhost)
        return;
    g_free(userhost->nick);
    g_free(userhost->ident);
    g_free(userhost->host);
    g_free(userhost);
}

int airc_get_flag_from_statuschar(IRC_conn *irc, char statuschar){
    char *cs, *fs;
    int shift;
    
    if(!(fs = irc->features.statusmsg) || !statuschar)
        return 0;
    cs = strchr(irc->features.statusmsg, statuschar);
    if(!cs)
        return 0;
    shift = fs+strlen(fs)-cs;
    return (AIRC_CBSTATUSMODE_BASE<<(shift))|(1<<((shift < 0x4) ? shift-1 : 0x3));
}

int airc_get_mode_class(IRC_conn *irc, char mode){
    char *default_modes[] = {"beI", "k", "fl", "MNOQRSTcimnprstuz", NULL};
    char *default_prefix = "aohv";
    char **modes, *prefix;
    int i;
    
    modes = (irc->features.chanmodes) ? irc->features.chanmodes : default_modes;
    prefix = (irc->features.prefix) ? irc->features.prefix : default_prefix;
    if(strchr(prefix, mode))
        return 1;
    for(i = 0; i < 4; i++){
        if(!modes[i])
            break;
        if(strchr(modes[i], mode))
            return i;
    }
    return 3;
}

int airc_get_flag_from_mode(IRC_conn *irc, char mode){
    char *default_prefix = "aohv", *default_statusmsg = "&@%+";
    char *prefix, *modePos, *statusmsg;
    
    prefix = (irc->features.prefix) ? irc->features.prefix : default_prefix;
    statusmsg = (irc->features.statusmsg) ? irc->features.statusmsg :
        default_statusmsg;
    modePos = strchr(prefix, mode);
    if(!modePos)
        return 0;
    return airc_get_flag_from_statuschar(irc, statusmsg[modePos-prefix]);
}